"""Spawn a cronjob to keep krb ticket valid."""
from datetime import timedelta
from datetime import timezone
import os
import re
import subprocess

from dateutil import parser as date_parser
import sentry_sdk

from . import cronjob
from . import misc
from . import retrying
from .logger import get_logger

LOGGER = get_logger('cki_lib.krb_ticket_refresher')


class RefreshKerberosTicket(cronjob.CronJob):
    """Issue a new krb ticket every hour."""

    schedule = '0 * * * *'

    def run(self, **_):
        """Run job."""
        if self._close_to_expire_ticket():
            LOGGER.info('Kerberos renewal time expires in < 10 h')
            LOGGER.info('Creating a new Kerberos ticket')
            self._kinit_new()
            return
        self._kinit_renew()

    @staticmethod
    def _close_to_expire_ticket(renewal_threshold_hours=10):
        """Return True if the renewal lifetime expires in < 10 h."""
        output = subprocess.run(
            ["/usr/bin/klist"],
            check=True,
            stdout=subprocess.PIPE,
            encoding='utf8',
        ).stdout
        if not (match := re.search(r'renew until\s+(.*)$', output, re.MULTILINE)):
            LOGGER.warning("Unable to parse 'klist' output.")
            return False

        renew_until_str = match.group(1)
        renew_until = date_parser.parse(renew_until_str).replace(tzinfo=timezone.utc)
        time_difference = renew_until - misc.now_tz_utc()
        return time_difference < timedelta(hours=renewal_threshold_hours)

    @staticmethod
    @retrying.retrying_on_exception(subprocess.CalledProcessError)
    def _kinit_renew():
        """Renew an existent ticket."""
        subprocess.run(['/usr/bin/kinit', '-R'], check=True)

    @staticmethod
    @retrying.retrying_on_exception(subprocess.CalledProcessError)
    def _kinit_new():
        """Request a new ticket."""
        keytab = os.environ.get('KRB_KEYTAB', '/keytab')
        user = os.environ.get('KRB_USER')
        subprocess.run(['/usr/bin/kinit', '-t', keytab, user, '-l', '7d'],
                       check=True)


if __name__ == '__main__':
    misc.sentry_init(sentry_sdk)
    cronjob.run([RefreshKerberosTicket()])
